package com.open.validator.impl;

import com.open.validator.MaxExt;
import org.apache.commons.beanutils.BeanUtils;
import org.hibernate.validator.internal.constraintvalidators.bv.number.InfinityNumberComparatorHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;


/**
 * @author: system
 * @Date: 2024/4/19 15:10
 * @Version: V1.0
 * @description: desc:
 */

public class MaxExtValidator implements ConstraintValidator<MaxExt, Object> {

    private static final Logger log = LoggerFactory.getLogger(MaxExtValidator.class);

    protected long maxValue;
    protected String field;
    protected String depField;
    private String[] depValue;

    @Override
    public void initialize(MaxExt maxTypeValue) {
        this.maxValue = maxTypeValue.value();
        this.field = maxTypeValue.field();
        this.depField = maxTypeValue.depField();
        this.depValue = maxTypeValue.depValue();
    }

    /**
     * field,depField,depValue三个属性其一为null值，不进行校验；<br>
     * 约束字段和依赖字段的值其一为null值，不进行校验；<br>
     * depValue不等于依赖字段的值，不进行校验
     */
    @Override
    public boolean isValid(Object object, ConstraintValidatorContext constraintValidatorContext) {
        if (object == null || field == null || depField == null || depValue == null) {
            return true;
        }

        try {
            // 获取本字段的值
            String checkedValue = BeanUtils.getProperty(object, field);
            // null values are valid
            if (checkedValue == null) {
                return true;
            }
            // 获取依赖字段的值
            String requiredValue = BeanUtils.getProperty(object, depField);
            if (requiredValue == null) {
                return true;
            }
            // 依赖字段的值包含在指定的值中
            if (depValue.length == 0 || Arrays.asList(depValue).contains(requiredValue)) {

                var cls = object.getClass().getDeclaredField(field).getType();
                String name = cls.getName();

                if (cls == Long.class) {

                    var valueOf = Long.valueOf(checkedValue);
                    return (NumberComparatorHelper.compare(valueOf, maxValue)) <= 0;

                } else if (cls == Double.class || "double".equals(name)) {

                    var valueOf = Double.valueOf(checkedValue);
                    return (NumberComparatorHelper.compare(valueOf, maxValue,
                            InfinityNumberComparatorHelper.GREATER_THAN)) <= 0;

                } else if (cls == Float.class || "float".equals(name)) {
                    var valueOf = Float.valueOf(checkedValue);
                    return (NumberComparatorHelper.compare(valueOf, maxValue,
                            InfinityNumberComparatorHelper.GREATER_THAN)) <= 0;

                } else if (Number.class.isAssignableFrom(cls) || "byte".equals(name) || "short".equals(name)
                        || "int".equals(name) || "long".equals(name)) {

                    var valueOf = Long.valueOf(checkedValue);
                    return (NumberComparatorHelper.compare(valueOf, maxValue)) <= 0;

                } else {
                    log.error("field:{} type not allowed", name);
                    return false;
                }

            } else {
                return true;
            }

        } catch (IllegalAccessException e) {
            log.error("Accessor method is not available for class : {}, exception : {}", object.getClass().getName(), e);
            return false;
        } catch (NoSuchMethodException e) {
            log.error("Field or method is not present on class : {}, exception : {}", object.getClass().getName(), e);
            return false;
        } catch (InvocationTargetException e) {
            log.error("An exception occurred while accessing class : {}, exception : {}", object.getClass().getName(), e);
            return false;
        } catch (NoSuchFieldException e) {
            log.error("Signals that the class doesn't have a field of a specified name : {}, exception : {}", object.getClass().getName(), e);
            return false;

        }

    }

}
